/* $Id$ */
package ibis.ipl.impl.stacking.lrmc.io;
import ibis.io.Conversion;
import ibis.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
/**
* This is a complete implementation of <code>DataInputStream</code>. It is
* built on top of an <code>InputStream</code>. There is no need to put any
* buffering in between. This implementation does all the buffering needed.
*/
public final class BufferedArrayInputStream extends DataInputStream {
private static final int SIZEOF_BOOLEAN = 1;
private static final int SIZEOF_CHAR = 2;
private static final int SIZEOF_SHORT = 2;
private static final int SIZEOF_INT = 4;
private static final int SIZEOF_LONG = 8;
private static final int SIZEOF_FLOAT = 4;
private static final int SIZEOF_DOUBLE = 8;
private static boolean DEBUG = false;
/** The buffer size. */
private static final int BUF_SIZE = 8 * 1024;
/** The underlying <code>InputStream</code>. */
private LrmcInputStream in;
/** The buffer. */
private byte[] buffer = new byte[BUF_SIZE];
private int index, buffered_bytes;
/** Number of bytes read so far from the underlying layer. */
private long bytes = 0;
/** Object used to convert primitive types to bytes. */
private Conversion conversion;
public BufferedArrayInputStream(LrmcInputStream in) {
this.in = in;
conversion = Conversion.loadConversion(false);
}
public int bufferSize() {
return BUF_SIZE;
}
public void setInputStream(LrmcInputStream in) {
this.in = in;
}
public LrmcInputStream getInputStream() {
return in;
}
public long bytesRead() {
return bytes - buffered_bytes;
}
public void resetBytesRead() {
bytes = buffered_bytes;
}
private static final int min(int a, int b) {
return (a > b) ? b : a;
}
public final int read() throws IOException {
try {
byte b = readByte();
return (b & 0377);
} catch (EOFException e) {
return -1;
}
}
private final void fillBuffer(int len) throws IOException {
// This ensures that there are at least 'len' bytes in the buffer
// PRECONDITION: 'index + buffered_bytes' should never be larger
// than BUF_SIZE!!
if (buffered_bytes >= len) {
return;
}
if (buffered_bytes == 0) {
index = 0;
} else if (index + buffered_bytes > BUF_SIZE - len) {
// not enough space for "len" more bytes
System.arraycopy(buffer, index, buffer, 0, buffered_bytes);
index = 0;
}
while (buffered_bytes < len) {
// System.err.println("buffer -> filled from " + index + " with "
// + buffered_bytes + " size " + BUF_SIZE + " read " + len);
int n = in.read(buffer, index + buffered_bytes, BUF_SIZE
- (index + buffered_bytes));
if (n < 0) {
throw new java.io.EOFException("EOF encountered");
}
bytes += n;
buffered_bytes += n;
}
}
public final int available() throws IOException {
return buffered_bytes;
}
public void readArray(boolean[] a, int off, int len) throws IOException {
if (DEBUG) {
System.err.println("readArray(boolean[" + off + " ... "
+ (off + len) + "])");
}
int useable, converted;
int to_convert = len * SIZEOF_BOOLEAN;
while (buffered_bytes < to_convert) {
// not enough data in the buffer
if (buffered_bytes == 0) {
index = 0;
fillBuffer(min(BUF_SIZE, to_convert));
} else {
// first, copy the data we do have to 'a' .
useable = buffered_bytes / SIZEOF_BOOLEAN;
conversion.byte2boolean(buffer, index, a, off, useable);
len -= useable;
off += useable;
converted = useable * SIZEOF_BOOLEAN;
index += converted;
buffered_bytes -= converted;
to_convert -= converted;
// second, copy the leftovers to the start of the buffer.
for (int i = 0; i < buffered_bytes; i++) {
buffer[i] = buffer[index + i];
}
index = 0;
// third, fill the buffer as far as possible.
fillBuffer(min(BUF_SIZE, to_convert));
}
}
// enough data in the buffer
conversion.byte2boolean(buffer, index, a, off, len);
buffered_bytes -= to_convert;
index += to_convert;
}
public void readArray(byte[] a, int off, int len) throws IOException {
if (DEBUG) {
System.err.println("readArray(byte[" + off + " ... " + (off + len)
+ "])");
}
if (buffered_bytes >= len) {
// System.err.println("IN BUF");
// data is already in the buffer.
// System.err.println("Data is in buffer -> copying " + index +
// " ... " + (index+len) + " to " + off);
System.arraycopy(buffer, index, a, off, len);
index += len;
buffered_bytes -= len;
// System.err.println("DONE");
} else {
if (buffered_bytes != 0) {
// System.err.println("PARTLY IN BUF " + buffered_bytes
// + " " + len);
// first, copy the data we do have to 'a' .
System.arraycopy(buffer, index, a, off, buffered_bytes);
}
int rd = buffered_bytes;
index = 0;
do {
int n = in.read(a, off + rd, len - rd);
if (n < 0) {
throw new java.io.EOFException("EOF encountered");
}
rd += n;
bytes += n;
} while (rd < len);
buffered_bytes = 0;
}
// System.err.print("result -> byte[");
// for (int i=0;i<len;i++) {
// System.err.print(a[off+i] + ",");
// }
// System.err.println("]");
}
// static int R = 0;
// static int W = 0;
public void readArray(short[] a, int off, int len) throws IOException {
int useable, converted;
int to_convert = len * SIZEOF_SHORT;
while (buffered_bytes < to_convert) {
// not enough data in the buffer
if (buffered_bytes == 0) {
index = 0;
fillBuffer(min(BUF_SIZE, to_convert));
} else {
// first, copy the data we do have to 'a' .
useable = buffered_bytes / SIZEOF_SHORT;
conversion.byte2short(buffer, index, a, off, useable);
len -= useable;
off += useable;
converted = useable * SIZEOF_SHORT;
index += converted;
buffered_bytes -= converted;
to_convert -= converted;
// second, copy the leftovers to the start of the buffer.
for (int i = 0; i < buffered_bytes; i++) {
buffer[i] = buffer[index + i];
}
index = 0;
// third, fill the buffer as far as possible.
fillBuffer(min(BUF_SIZE, to_convert));
}
}
// enough data in the buffer
conversion.byte2short(buffer, index, a, off, len);
buffered_bytes -= to_convert;
index += to_convert;
if (DEBUG) {
System.err.print("readArray(short[");
for (int i = 0; i < len; i++) {
System.err.print(a[off + i] + ",");
}
System.err.println("]");
System.err.flush();
}
}
public void readArray(char[] a, int off, int len) throws IOException {
if (DEBUG) {
System.err.println("readArray(char[" + off + " ... " + (off + len)
+ "])");
}
int useable, converted;
int to_convert = len * SIZEOF_CHAR;
while (buffered_bytes < to_convert) {
// not enough data in the buffer
if (buffered_bytes == 0) {
index = 0;
fillBuffer(min(BUF_SIZE, to_convert));
} else {
// first, copy the data we do have to 'a' .
useable = buffered_bytes / SIZEOF_CHAR;
conversion.byte2char(buffer, index, a, off, useable);
len -= useable;
off += useable;
converted = useable * SIZEOF_CHAR;
index += converted;
buffered_bytes -= converted;
to_convert -= converted;
// second, copy the leftovers to the start of the buffer.
for (int i = 0; i < buffered_bytes; i++) {
buffer[i] = buffer[index + i];
}
index = 0;
// third, fill the buffer as far as possible.
fillBuffer(min(BUF_SIZE, to_convert));
}
}
// enough data in the buffer
conversion.byte2char(buffer, index, a, off, len);
buffered_bytes -= to_convert;
index += to_convert;
}
public void readArray(int[] a, int off, int len) throws IOException {
if (DEBUG) {
System.err.println("readArray(int[" + off + " ... " + (off + len)
+ "])");
}
int useable, converted;
int to_convert = len * SIZEOF_INT;
// System.err.println("To convert " + to_convert);
// System.err.println("Buffered " + buffered_bytes);
while (buffered_bytes < to_convert) {
// not enough data in the buffer
if (buffered_bytes == 0) {
index = 0;
fillBuffer(min(BUF_SIZE, to_convert));
} else {
// first, copy the data we do have to 'a' .
useable = buffered_bytes / SIZEOF_INT;
// System.err.println("converting " + useable + " ints from "
// + off);
conversion.byte2int(buffer, index, a, off, useable);
len -= useable;
off += useable;
converted = useable * SIZEOF_INT;
index += converted;
buffered_bytes -= converted;
to_convert -= converted;
// System.err.println("Leftover " + len + " ints to convert, "
// + buffered_bytes + " bytes buffered"
// + to_convert + " bytes to convert");
// second, copy the leftovers to the start of the buffer.
for (int i = 0; i < buffered_bytes; i++) {
buffer[i] = buffer[index + i];
}
index = 0;
// third, fill the buffer as far as possible.
fillBuffer(min(BUF_SIZE, to_convert));
}
}
// enough data in the buffer
// System.err.println("converting " + len + " ints from " + index
// + " to " + off);
conversion.byte2int(buffer, index, a, off, len);
buffered_bytes -= to_convert;
index += to_convert;
// System.err.println("Done converting int [], buffer contains "
// + buffered_bytes + " bytes (starting at " + index + ")");
}
public void readArray(long[] a, int off, int len) throws IOException {
if (DEBUG) {
System.err.println("readArray(long[" + off + " ... " + (off + len)
+ "])");
}
int useable, converted;
int to_convert = len * SIZEOF_LONG;
while (buffered_bytes < to_convert) {
// not enough data in the buffer
if (buffered_bytes == 0) {
index = 0;
fillBuffer(min(BUF_SIZE, to_convert));
} else {
// first, copy the data we do have to 'a' .
useable = buffered_bytes / SIZEOF_LONG;
conversion.byte2long(buffer, index, a, off, useable);
len -= useable;
off += useable;
converted = useable * SIZEOF_LONG;
index += converted;
buffered_bytes -= converted;
to_convert -= converted;
// second, copy the leftovers to the start of the buffer.
for (int i = 0; i < buffered_bytes; i++) {
buffer[i] = buffer[index + i];
}
index = 0;
// third, fill the buffer as far as possible.
fillBuffer(min(BUF_SIZE, to_convert));
}
}
// enough data in the buffer
conversion.byte2long(buffer, index, a, off, len);
buffered_bytes -= to_convert;
index += to_convert;
}
public void readArray(float[] a, int off, int len) throws IOException {
if (DEBUG) {
System.err.println("readArray(float[" + off + " ... " + (off + len)
+ "])");
}
int useable, converted;
int to_convert = len * SIZEOF_FLOAT;
while (buffered_bytes < to_convert) {
// not enough data in the buffer
if (buffered_bytes == 0) {
index = 0;
fillBuffer(min(BUF_SIZE, to_convert));
} else {
// first, copy the data we do have to 'a' .
useable = buffered_bytes / SIZEOF_FLOAT;
conversion.byte2float(buffer, index, a, off, useable);
len -= useable;
off += useable;
converted = useable * SIZEOF_FLOAT;
index += converted;
buffered_bytes -= converted;
to_convert -= converted;
// second, copy the leftovers to the start of the buffer.
for (int i = 0; i < buffered_bytes; i++) {
buffer[i] = buffer[index + i];
}
index = 0;
// third, fill the buffer as far as possible.
fillBuffer(min(BUF_SIZE, to_convert));
}
}
// enough data in the buffer
conversion.byte2float(buffer, index, a, off, len);
buffered_bytes -= to_convert;
index += to_convert;
}
public void readArray(double[] a, int off, int len) throws IOException {
if (DEBUG) {
System.err.println("readArray(double[" + off + " ... "
+ (off + len) + "])");
}
int useable, converted;
int to_convert = len * SIZEOF_DOUBLE;
while (buffered_bytes < to_convert) {
// not enough data in the buffer
if (buffered_bytes == 0) {
index = 0;
fillBuffer(min(BUF_SIZE, to_convert));
} else {
// first, copy the data we do have to 'a' .
useable = buffered_bytes / SIZEOF_DOUBLE;
conversion.byte2double(buffer, index, a, off, useable);
len -= useable;
off += useable;
converted = useable * SIZEOF_DOUBLE;
index += converted;
buffered_bytes -= converted;
to_convert -= converted;
// second, copy the leftovers to the start of the buffer.
for (int i = 0; i < buffered_bytes; i++) {
buffer[i] = buffer[index + i];
}
index = 0;
// third, fill the buffer as far as possible.
fillBuffer(min(BUF_SIZE, to_convert));
}
}
// enough data in the buffer
conversion.byte2double(buffer, index, a, off, len);
buffered_bytes -= to_convert;
index += to_convert;
}
public byte readByte() throws IOException {
fillBuffer(1);
buffered_bytes--;
return buffer[index++];
}
public boolean readBoolean() throws IOException {
fillBuffer(1);
buffered_bytes--;
return conversion.byte2boolean(buffer[index++]);
}
public char readChar() throws IOException {
char v;
fillBuffer(SIZEOF_CHAR);
v = conversion.byte2char(buffer, index);
index += SIZEOF_CHAR;
buffered_bytes -= SIZEOF_CHAR;
return v;
}
public short readShort() throws IOException {
short v;
fillBuffer(SIZEOF_SHORT);
v = conversion.byte2short(buffer, index);
index += SIZEOF_SHORT;
buffered_bytes -= SIZEOF_SHORT;
return v;
}
public int readInt() throws IOException {
int v;
fillBuffer(SIZEOF_INT);
v = conversion.byte2int(buffer, index);
index += SIZEOF_INT;
buffered_bytes -= SIZEOF_INT;
return v;
}
public long readLong() throws IOException {
long v;
fillBuffer(SIZEOF_LONG);
v = conversion.byte2long(buffer, index);
index += SIZEOF_LONG;
buffered_bytes -= SIZEOF_LONG;
return v;
}
public float readFloat() throws IOException {
float v;
fillBuffer(SIZEOF_FLOAT);
v = conversion.byte2float(buffer, index);
index += SIZEOF_FLOAT;
buffered_bytes -= SIZEOF_FLOAT;
return v;
}
public double readDouble() throws IOException {
double v;
fillBuffer(SIZEOF_DOUBLE);
v = conversion.byte2double(buffer, index);
index += SIZEOF_DOUBLE;
buffered_bytes -= SIZEOF_DOUBLE;
return v;
}
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
public int read(byte[] a, int off, int len) throws IOException {
if (DEBUG) {
System.err.println("read(byte[" + off + " ... " + (off + len)
+ "])");
}
if (buffered_bytes >= len) {
// data is already in the buffer.
// System.err.println("Data is in buffer -> copying " + index +
// " ... " + (index+len) + " to " + off);
System.arraycopy(buffer, index, a, off, len);
index += len;
buffered_bytes -= len;
} else {
if (buffered_bytes != 0) {
// System.err.println("PARTLY IN BUF " + buffered_bytes
// + " " + len);
// first, copy the data we do have to 'a' .
System.arraycopy(buffer, index, a, off, buffered_bytes);
}
int rd = buffered_bytes;
index = 0;
do {
int n = in.read(a, off + rd, len - rd);
if (n < 0) {
len = rd;
} else {
rd += n;
bytes += n;
}
} while (rd < len);
buffered_bytes = 0;
}
// System.err.print("result -> byte[");
// for (int i=0;i<len;i++) {
// System.err.print(a[off+i] + ",");
// }
// System.err.println("]");
return len;
}
public void close() throws IOException {
in.close();
}
@Override
public void readByteBuffer(ByteBuffer value) throws IOException,
ReadOnlyBufferException {
int len = value.limit() - value.position();
if (buffered_bytes >= len) {
value.put(buffer, index, len);
index += len;
buffered_bytes -= len;
} else {
if (buffered_bytes != 0) {
value.put(buffer, index, buffered_bytes);
len -= buffered_bytes;
buffered_bytes = 0;
}
index = 0;
if (value.hasArray()) {
in.read(value.array(), value.arrayOffset(), len);
value.position(value.limit());
bytes += len;
} else {
do {
int toread = Math.min(len, BUF_SIZE);
fillBuffer(toread);
if (len < buffered_bytes) {
toread = len;
} else {
toread = buffered_bytes;
}
value.put(buffer, index, toread);
len -= toread;
index += toread;
buffered_bytes -= toread;
} while (len > 0);
}
}
}
}